package fr.lip6.meta.ple.featureIdentification;

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import artefact.generic.Artefact;

public class DpFeatureExtraction {

	private Collection<Feature> features = null;
	private Collection<Product> AllP = null;
	
	public Collection<Feature> extract(Collection<Artefact> artefacts) {
		AllP = new ArrayList<Product>();
		int id = 1;
		
		for (Artefact a : artefacts) {
			Product p = new Product(ArtefactToTriggers.extract(a), id++);
			p.removeRepeated();
			AllP.add(p);
		}
		
		features = featureIdentification(AllP);
		
		return features;
	}
	
	/**
	 * Prints in a file the features information
	 * @param fileName - the name of the destination file
	 * @throws NullRootException
	 * @throws IOException
	 */
	public void featuresToFile(String fileName) throws NullRootException,
			IOException {
		if (features == null) throw new NullRootException();
        FileWriter fw = new FileWriter(fileName);;
        PrintWriter pw = new PrintWriter(fw);
        int pSize = AllP.size();
        
//        canonizeFeatures((List<Feature>)features);
        identifyFeatures(features);
        
        //Printing information
		pw.println("PRODUCTS - FEATURES:");
        for (Product prod : AllP)
        	pw.println(prod.getId() + ": " +
        			prod.featuresImplementedString(features));
        pw.println("\n\nPRODUCTS: " + pSize + "\n");
        for (Product prod : AllP)
        	pw.println(prod);
        pw.println("\n\nFEATURES: " + features.size() + "\n");
        for (Feature f : features) {
        	Collection<Integer> p = f.getProdIds();
        	int rate = (p.size() * 100) / pSize;
        	pw.println(f.getId() + " [rate " + rate + "%]: " + p);
        	pw.println(f.toStringLines(1));
        }
        fw.close();
	}

	//@SuppressWarnings("unchecked")
	private Collection<Feature> featureIdentification(Collection<Product> AllP) {
		ArrayList<Feature> features = new ArrayList<Feature>();
		ArrayList<Feature> inter = initInter(AllP);
		int nProducts = AllP.size();
		
		for (int k = 2; k <= nProducts; k++) {
//			System.out.println("k: " + k);
			ArrayList<Feature> inter0 = new ArrayList<Feature>();
			for (Feature f : inter) {
				ArrayList<Product> selectedProducts = selectGreater(AllP, f);
/*				
				//debug...
				System.out.print("inter f " + f.getProdIds() + " avec products ");
				for (Product p : selectedProducts) {
					System.out.print(p.getId() + " ");
				}
				System.out.println();
				//..
*/				
				for (Product p : selectedProducts) {
					add(inter0, p.intersection(f));
				}
			}
			inter = inter0;
			add(features, inter);
		}
		
		remainders(AllP, features);
		return features;
	}

	private ArrayList<Feature> initInter(Collection<Product> AllP) {
		ArrayList<Feature> inter = new ArrayList<Feature>();
		for (Product p : AllP) {
			inter.add(Feature.featureFromProduct(p));
		}
		return inter;
	}
	
	private void add(Collection<Feature> inter, Feature f) {
		if (f == null) return;
		if (f.isEmpty()) return;
		Feature eq;
		if ((eq = f.getEquivalent(inter)) != null) {
			eq.prodIds.addAll(f.prodIds);
			return;
		}
		
		inter.add(f);
	}
	
	private void add(Collection<Feature> features, Collection<Feature> inter) {
		Feature eq;
		for (Feature f : inter) {
			if ((eq = f.getEquivalent(features)) == null)
				features.add(f);
			else
				eq.prodIds.addAll(f.prodIds);
		}
	}
	
	private ArrayList<Product> selectGreater(Collection<Product> products, 
			Feature f) {
		
		ArrayList<Product> greaterProducts = new ArrayList<Product>();
		int maxId = maxProductId(f);
		
		for (Product p : products)
			if (p.getId() > maxId)
				greaterProducts.add(p);
		
		return greaterProducts;
	}
	
	private int maxProductId(Feature f) {
		int max = Integer.MIN_VALUE;
		for (int id : f.getProdIds())
			if (id > max) max = id;
		return max;
	}
	
	private void remainders(Collection<Product> products,
			Collection<Feature> features) {
		
		for (Product p : products) {
			Feature r = Feature.featureFromProduct(p);
			for (Feature f: features)
				r.removeAll(f);
			if (!r.isEmpty())
				features.add(r);
		}
	}
	
	/**
	 * Sets an id for each feature
	 * @param features
	 */
	private void identifyFeatures(Collection<Feature> features) {
		int id = 0;
		for (Feature f : features) {
			f.setId("F" + id++);
		}
	}
	
	@SuppressWarnings("unused")
	private void canonizeFeatures(List<Feature> features) {
		ArrayList<Feature> forRemove = new ArrayList<Feature>();
		for (int i = 0; i < features.size() - 1; i++) {
			forRemove.clear();
			Feature fi = features.get(i);
			for (int j = i + 1; j < features.size(); j++) {
				Feature fj = features.get(j);
				Feature norm = fj.canonize(fi);
				if (norm != null) {
					features.add(norm);
					if (fj.isEmpty()) forRemove.add(fj);
					if (fi.isEmpty()) {
						forRemove.add(fi);
						i--;
					}
				}
			}
			features.removeAll(forRemove);
		}
	}
	
}
